﻿/*
 * @author: wizardc
 */

using System;
using System.Collections.Generic;
using Dou.Clock;
using Dou.Utils;

namespace Dou.Timer
{
    /// <summary>
    /// 计时器回调委托
    /// </summary>
    public delegate void TimerCallback(params object[] args);

    /// <summary>
    /// 计时器管理类
    /// </summary>
    public static class TimerManager
    {
        private static readonly Timer _realTimer;
        private static readonly Timer _unscaledTimer;
        private static readonly Timer _timer;

        private static List<WaitFrameInfo> _waitFrameList;
        private static List<WaitFrameInfo> _tempWaitFrameList;

        static TimerManager()
        {
            Recyclable.Register<WaitFrameInfo>(50, () => new WaitFrameInfo());

            _realTimer = new Timer();
            ClockUtil.realTimeClock.AddClient(_realTimer);
            _unscaledTimer = new Timer();
            ClockUtil.unscaledTimeClock.AddClient(_unscaledTimer);
            _timer = new Timer();
            ClockUtil.timeClock.AddClient(_timer);

            _waitFrameList = new List<WaitFrameInfo>();
            _tempWaitFrameList = new List<WaitFrameInfo>();
            AbstractGameMain.lateUpdateEvent += OnLateUpdate;
        }

        private static Timer GetTime(ClockTimeType type)
        {
            switch (type)
            {
                case ClockTimeType.RealTime:
                    return _realTimer;
                case ClockTimeType.UnscaledTime:
                    return _unscaledTimer;
            }
            return _timer;
        }

        public static int SetTimeout(ClockTimeType timeType, float time, TimerCallback callback, params object[] args)
        {
            return SetRepeat(timeType, 1, time, callback, args);
        }

        public static void ClearTimeout(int id)
        {
            ClearRepeat(id);
        }

        public static int SetInterval(ClockTimeType timeType, float time, TimerCallback callback, params object[] args)
        {
            return SetRepeat(timeType, -1, time, callback, args);
        }

        public static void ClearInterval(int id)
        {
            ClearRepeat(id);
        }

        public static int SetRepeat(ClockTimeType timeType, int repeatCount, float time, TimerCallback callback, params object[] args)
        {
            Timer timer = GetTime(timeType);
            int id = timer.AddTimer(repeatCount, time, callback, args);
            return id * 10 + (int) timeType;
        }

        public static void ClearRepeat(int id)
        {
            int tid = id / 10;
            int type = id % 10;
            ClockTimeType timeType = (ClockTimeType) Enum.Parse(typeof(ClockTimeType), type.ToString());
            Timer timer = GetTime(timeType);
            timer.RemoveTimer(tid);
        }

        public static void CallLater(TimerCallback callback, params object[] args)
        {
            WaitFrame(1, callback, args);
        }

        public static void WaitFrame(int waitFrame, TimerCallback callback, params object[] args)
        {
            if (waitFrame <= 0)
            {
                return;
            }
            WaitFrameInfo waitFrameInfo = Recyclable.Get<WaitFrameInfo>();
            waitFrameInfo.waitFrame = waitFrame;
            waitFrameInfo.callback = callback;
            waitFrameInfo.args = args;
            _waitFrameList.Add(waitFrameInfo);
        }

        private static void OnLateUpdate()
        {
            List<WaitFrameInfo> temp = _waitFrameList;
            _waitFrameList = _tempWaitFrameList;
            _tempWaitFrameList = temp;

            for (int i = 0; i < _tempWaitFrameList.Count; i++)
            {
                WaitFrameInfo waitFrame = _tempWaitFrameList[i];
                if (--waitFrame.waitFrame <= 0)
                {
                    waitFrame.callback(waitFrame.args);
                    Recyclable.Recycle(waitFrame);
                }
                else
                {
                    _waitFrameList.Add(waitFrame);
                }
            }

            _tempWaitFrameList.Clear();
        }
    }

    class WaitFrameInfo : ICacheable
    {
        public int waitFrame;
        public TimerCallback callback;
        public object[] args;

        public void OnRecycle()
        {
            waitFrame = 0;
            callback = null;
            args = null;
        }

        public void OnReuse()
        {
        }
    }
}
